FlutterでAPIをコールしてデータを表示して見た
大阪オフィスの山田です。育てている楓を一度枯らしてしまったのですが、幹から新しい芽が出てきました。今回はFlutterでシンプルにAPIから取得したデータを表示してみます。入力された文字を名前に含むGitHubリポジトリを検索して、リストにして表示するプログラムを実装してみました。ビジネスロジックを分けることもしていないので、大きなWidgetの中でベタに書いています。GitHubのAPIに関するドキュメントはこちら。
開発環境
flutter doctor
[✓] Flutter (Channel master, v0.9.5-pre.12, on Mac OS X 10.13.6 17G65, locale ja-JP) [✓] Android toolchain - develop for Android devices (Android SDK 28.0.1) [✓] iOS toolchain - develop for iOS devices (Xcode 10.0) [✓] Android Studio (version 3.1) ✗ Flutter plugin not installed; this adds Flutter specific functionality. ✗ Dart plugin not installed; this adds Dart specific functionality. [✓] VS Code (version 1.27.2) [✓] Connected device (1 available)
Android Studioは確かバージョン3.2が来ていましたね。近いうちにアップデートしよう。詳細はこちら。
動作画像
iOS
Android
実装
Model
Githubのリポジトリ情報をModelにします。Named Constructorを追加して、JSONからModelを生成できるようにしています。Named Constructorについてはこちら
class GithubRepository { /// Repository full name. final String fullName; /// Repository description. final String description; /// Language in use. final String language; /// Repository html url. final String htmlUrl; /// Count of stars. final int stargazersCount; /// Count of watchers. final int watchersCount; /// Count of forks repository. final int forksCount; GithubRepository.fromJson(Map<String, dynamic> json) : fullName = json['full_name'], description = json['description'], language = json['language'], htmlUrl = json['html_url'], stargazersCount = json['stargazers_count'], watchersCount = json['watchers_count'], forksCount = json['forks_count']; }
入力部分
Widget _buildInput() { return Container( margin: EdgeInsets.all(16.0), child: TextField( decoration: InputDecoration( prefixIcon: Icon(Icons.search), hintText: 'Please enter a search repository name.', labelText: "search" ), onChanged: (inputString) { if (inputString.length >= 5) { _searchRepositories(inputString).then((repositories) { setState(() { _repositories = repositories; }); }); } }, ) ); }
TextFieldを置いて、onChangedイベントで入力文字数が5文字以上になったら、APIをコールするメソッドを呼んでいます。取得したGithubRepositoryの一覧を更新して、再描画しています。_repositories
はState内に定義してある変数です。※エラー処理は入れてません。
APIコール部分
Future<List<GithubRepository>> _searchRepositories(String searchWord) async { final response = await http.get('https://api.github.com/search/repositories?q=' + searchWord + '&sort=stars&order=desc'); if (response.statusCode == 200) { List<GithubRepository> list = []; Map<String, dynamic> decoded = json.decode(response.body); for (var item in decoded['items']) { list.add(GithubRepository.fromJson(item)); } return list; } else { throw Exception('Fail to search repository'); } }
APIコール部分です。レスポンスが返ってきたらJSONからGithubRepositoryの配列に変換して返却しています。
リスト部分
少し長いですが、ほとんどがデザイン部分です。
Widget _buildRepositoryList() { return ListView.builder( itemBuilder: (BuildContext context, int index) { final repository = _repositories[index]; return _buildCard(repository); }, itemCount: _repositories.length, ); } Widget _buildCard(GithubRepository repository) { return Card( margin: EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Padding( padding: EdgeInsets.all(12.0), child: Text( repository.fullName, style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16.0 ), ), ), repository.language != null ? Padding( padding: EdgeInsets.fromLTRB(12.0, 0.0, 12.0, 12.0), child: Text( repository.language, style: TextStyle( fontWeight: FontWeight.bold, fontSize: 12.0 ), ), ) : Container(), repository.description != null ? Padding( padding: EdgeInsets.fromLTRB(12.0, 0.0, 12.0, 12.0), child: Text( repository.description, style: TextStyle( fontWeight: FontWeight.w200, color: Colors.grey ) ), ) : Container(), Row( mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ Icon(Icons.star), SizedBox( width: 50.0, child: Text(repository.stargazersCount.toString()), ), Icon(Icons.remove_red_eye), SizedBox( width: 50.0, child: Text(repository.watchersCount.toString()), ), Text("Fork:"), SizedBox( width: 50.0, child: Text(repository.forksCount.toString()), ), ], ), SizedBox(height: 16.0,) ], ) ,); }
リスト部分を作成しています。State内に宣言されているGithubRepositoryの配列をCard Widgetにして表示しています。スター数などのテキストは、SizedBoxで大きさを揃えています。Forkに該当する良いアイコンが見つからなかった...
最後に
いかがだったでしょうか。今回はベタがきでシンプルに実装しました。ビジネスロジックがViewの中に紛れ込んでしまっていますね。現在、BLoCパターンを勉強中ですので、そちらのアーキテクチャパターンを使って実装し直して、ブログにしようと考えています。